import { h } from 'ja-contextmenu';
import './style.less';
import Flip from './utils/Flip';

class SawPuzzle {
  app = document.querySelector('#app');
  config = {
    // splitCols: 4,
    splitRows: 2,
  };
  earSize = 10;
  params = {
    imageAspectRatio: 1,
    imageWidth: 800,
    imageHeight: 600,
  };

  el: Record<string, HTMLElement | null> = {
    container: null,
    matrix: null,
  };
  /** 选中的块 */
  selectedPieceEl: HTMLElement | null = null;
  pieces: HTMLElement[] = [];

  movingEle: HTMLElement | null = null;
  hoverEle: HTMLElement | null = null;
  flip: Flip | null = null;

  async init() {
    const image = new Image();
    image.src = '../public/ff13.jpg';

    await new Promise<void>(resolve => {
      image.onload = () => {
        // console.log(image.width, image.height);
        this.params.imageWidth = image.width;
        this.params.imageHeight = image.height;
        this.params.imageAspectRatio = this.params.imageWidth / this.params.imageHeight;
        resolve();
      };
    });
    const imageWidth = 800;
    const imageHeight = imageWidth / this.params.imageAspectRatio;

    const pieceSize = imageHeight / this.config.splitRows;

    this.earSize = pieceSize * 0.1;

    const { splitRows } = this.config;
    const splitCols = Math.floor(imageWidth / pieceSize);
    const pieces = [];
    for (let row = 0; row < splitRows; row++) {
      for (let col = 0; col < splitCols; col++) {
        const pieceDom = h('div.piece');

        pieceDom.dataset.pieceIndex = String(row * (splitRows + 1) + col); //TODO:
        pieceDom.dataset.direction = 'up';
        pieceDom.dataset.rowIndex = String(row);
        pieceDom.dataset.colIndex = String(col);

        if (row % 2) {
          pieceDom.classList.add(col % 2 ? 'vertical' : 'horizontal');
          pieceDom.dataset.pieceType = col % 2 ? 'v' : 'h';
        } else {
          pieceDom.classList.add(col % 2 ? 'horizontal' : 'vertical');
          pieceDom.dataset.pieceType = col % 2 ? 'h' : 'v';
        }
        // 边缘piece标记
        if (row === 0) {
          pieceDom.classList.add('edge-top');
        }
        if (col === 0) {
          pieceDom.classList.add('edge-left');
        }
        if (col === splitCols - 1) {
          pieceDom.classList.add('edge-right');
        }
        if (row === splitRows - 1) {
          pieceDom.classList.add('edge-bottom');
        }

        if (pieceDom.classList.contains('vertical')) {
          pieceDom.style.left = col * pieceSize + 'px';
          pieceDom.style.top = row * pieceSize - this.earSize + 'px';
          // pieceDom.style.transform = `translate(${col * pieceSize}px,${row * pieceSize - earSize}px)`;
          pieceDom.style.setProperty('--x', -col * pieceSize + 'px');
          pieceDom.style.setProperty('--y', -row * pieceSize + this.earSize + 'px');
        } else {
          pieceDom.style.left = col * pieceSize - this.earSize + 'px';
          pieceDom.style.top = row * pieceSize + 'px';
          pieceDom.style.setProperty('--x', -col * pieceSize + this.earSize + 'px');
          pieceDom.style.setProperty('--y', -row * pieceSize + 'px');
          // pieceDom.style.transform = `translate(${col * pieceSize - earSize}px,${row * pieceSize}px)`;
        }

        pieceDom.draggable = true;

        pieces.push(pieceDom);
      }
    }
    this.flip = new Flip(pieces, { duration: 0.5, cloneNode: false });
    this.pieces = pieces;

    const matrix = h('div.matrix', pieces);
    matrix.style.setProperty('--ear-size', this.earSize + 'px');
    matrix.style.setProperty('--img-w', imageWidth + 'px');
    matrix.style.setProperty('--img-h', imageHeight + 'px');
    matrix.style.setProperty('--piece-size', pieceSize + 'px');

    image.height = 200;
    image.className = 'origin-image';

    const container = h('div.container', [image, matrix]);

    this.el.matrix = matrix;
    this.addPieceDragEvent();
    this.addPieceSelectEvent();
    this.addRotateEvent();
    this.app?.appendChild(container);
    this.disruptPieces();
  }

  /** 打乱 */
  disruptPieces() {
    this.flip?.record();
    const len = this.pieces.length;
    const newPiecesArr = [...this.pieces].sort(() => {
      return Math.random() - 0.5;
    });
    let disruptCount = 0;
    const recursion = () => {
      disruptCount += 1;
      newPiecesArr.forEach(item => {
        const i = Number(item.dataset.pieceIndex as string);
        const randomIndex = Math.round(Math.random() * (len - i - 1) + i);
        this.switchPiece(item, newPiecesArr[randomIndex]);
      });
      this.flip?.play();
      if (disruptCount < 3) {
        window.setTimeout(recursion, 500);
      }
    };

    window.setTimeout(recursion, 1000);
  }

  addPieceSelectEvent() {
    this.el.matrix?.addEventListener('click', e => {
      const el = e.target as HTMLElement;
      if (el.classList.contains('piece')) {
        this.el.matrix?.querySelectorAll('.piece.selected').forEach(p => p.classList.remove('selected'));
        el.classList.add('selected');
        this.selectedPieceEl = el;
      }
    });
  }

  addRotateEvent() {
    const degExp = /[0-9]+deg/;
    window.addEventListener('contextmenu', e => {
      e.preventDefault();
      if (!this.selectedPieceEl) return;
      let transform = this.selectedPieceEl.style.transform;
      const oldRotate = transform?.match(degExp);
      if (!oldRotate) {
        transform += `rotate(90deg)`;
      } else {
        const oldRotateDeg = parseInt(oldRotate?.[0] || '0');
        transform = transform.replace(degExp, (oldRotateDeg === 360 ? 0 : oldRotateDeg) + 90 + 'deg');
      }
      this.selectedPieceEl.style.transform = transform;
    });
  }

  addPieceDragEvent() {
    this.pieces.forEach(dom => {
      dom.addEventListener('dragstart', e => {
        if (e.target === this.el.matrix) return;
        e.dataTransfer!.effectAllowed = 'move'; // 鼠标样式，标志为移动
        // drag出去的样式是当前的样式，因此要异步设置当前元素样式。
        const targetEl = e.target as HTMLElement;
        setTimeout(() => {
          targetEl.classList.add('move');
        });
        this.movingEle = targetEl;
        this.flip?.record();
      });
      dom.addEventListener('dragover', e => {
        e.preventDefault();
      });
      dom.addEventListener('dragenter', e => {
        e.preventDefault();
        const targetEl = e.target as HTMLElement;

        if (!this.el.matrix || !this.movingEle) return;
        if (targetEl === this.movingEle) return;

        this.hoverEle = targetEl;
        // const targetLi = e.target as HTMLElement;
        // const sourceIndex = this.pieces.indexOf(this.movingEle);
        // const targetIndex = this.pieces.indexOf(targetLi);

        // if (sourceIndex < targetIndex) {
        //   this.el.matrix.insertBefore(this.movingEle, targetLi?.nextElementSibling);
        // } else {
        //   this.el.matrix.insertBefore(this.movingEle, targetLi);
        // }
      });
      dom.addEventListener('dragend', e => {
        if (!this.movingEle || !this.hoverEle) return;
        const targetEl = e.target as HTMLElement;
        if (targetEl) {
          targetEl.classList.remove('move');
        }
        this.switchPiece(this.movingEle, this.hoverEle);

        this.movingEle = null;
        this.flip?.play();
        window.setTimeout(() => {
          this.checkFinish();
        }, 1000);
      });
    });
  }

  /**交换piece */
  switchPiece(a: HTMLElement, b: HTMLElement) {
    const sourceEleNext = a.nextElementSibling;

    const movingElePieceType = a.dataset.pieceType;
    const hoverElePieceType = b.dataset.pieceType;
    const temp = {
      left: a.style.left,
      top: a.style.top,
    };
    if (movingElePieceType === hoverElePieceType) {
      // switch position
      a.style.left = b.style.left;
      a.style.top = b.style.top;
      b.style.left = temp.left;
      b.style.top = temp.top;
    } else if (movingElePieceType === 'v') {
      // switch position
      a.style.left = parseFloat(b.style.left) + this.earSize + 'px';
      a.style.top = parseFloat(b.style.top) - this.earSize + 'px';
      b.style.left = parseFloat(temp.left) - this.earSize + 'px';
      b.style.top = parseFloat(temp.top) + this.earSize + 'px';
    } else if (movingElePieceType === 'h') {
      // switch position
      a.style.left = parseFloat(b.style.left) - this.earSize + 'px';
      a.style.top = parseFloat(b.style.top) + this.earSize + 'px';
      b.style.left = parseFloat(temp.left) + this.earSize + 'px';
      b.style.top = parseFloat(temp.top) - this.earSize + 'px';
    }

    //switch dom order
    this.el.matrix?.insertBefore(a, b.nextElementSibling);
    this.el.matrix?.insertBefore(b, sourceEleNext);
  }

  checkFinish() {
    if (!this.el.matrix) return;
    const pieces = Array.from(this.el.matrix.querySelectorAll<HTMLElement>('.piece'));
    const isFinish = pieces.every((piece, index) => {
      const pieceIndex = piece.dataset.pieceIndex;
      if (!pieceIndex) {
        throw new Error('no piece index');
      }
      const res = +pieceIndex === index && piece.dataset.direction === 'up';
      console.log(piece, pieceIndex, index, piece.dataset.direction);
      return res;
    });
    if (isFinish) {
      alert('完成');
    }
  }
}

const sawPuzzle = new SawPuzzle();
sawPuzzle.init();
